#pragma once
#include "../common/dispatcher.hpp"
#include "requestor.hpp"
#include "rpc_caller.hpp"
#include "rpc_registry.hpp"
#include "rpc_topic.hpp"

namespace rcrpc{
    namespace client {
        class RegistryClient {
            public:
                using ptr = std::shared_ptr<RegistryClient>;
                // 构造函数传入注册中心的地址信息，用于连接注册中心
                RegistryClient(const std::string& ip, int port) :
                    _requestor(std::make_shared<Requestor>()),
                    _provider(std::make_shared<client::Provider>(_requestor)),
                    _dispatcher(std::make_shared<Dispatcher>())
                    {
                        auto rsp_cb = std::bind(&client::Requestor::onResponse, _requestor.get(), 
                            std::placeholders::_1, std::placeholders::_2);
                        
                        // 当客户端收到一个服务响应时，  Requestor   会处理这个响应，例如解析返回的服务地址信息。
                        _dispatcher->registerHandler<BaseMessage>(MType::RSP_SERVICE, rsp_cb);

                        auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                            std::placeholders::_1, std::placeholders::_2);
                        _client = ClientFactory::create(ip, port);
                        _client->setMessageCallback(message_cb);
                        _client->connect(); // 连接注册中心
                    }
                    // 向外提供的服务注册接口
                    bool registerMethod(const std::string &method, const Address &host) {
                        return _provider->registerMethod(_client->connection(), method, host);
                    }
            private:
                Requestor::ptr _requestor;
                client::Provider::ptr _provider;
                Dispatcher::ptr _dispatcher;
                BaseClient::ptr _client;                   
        };

        // 当客户端收到一个服务请求时，  Discoverer   会处理这个请求，例如查找服务的地址信息并返回。
        class DiscoveryClient {
            public:
                using ptr = std::shared_ptr<DiscoveryClient>;
                // 构造函数传入注册中心的地址信息，用于连接注册中心
                DiscoveryClient(const std::string& ip, int port, const Discovery::OfflineCallback &cb) :
                    _requestor(std::make_shared<Requestor>()),
                    _discovery(std::make_shared<client::Discovery>(_requestor,cb)),
                    _dispatcher(std::make_shared<Dispatcher>())
                    {
                        auto rsp_cb = std::bind(&client::Requestor::onResponse, _requestor.get(), 
                            std::placeholders::_1, std::placeholders::_2);
                        
                        // 当客户端收到一个服务响应时，  Requestor   会处理这个响应，例如解析返回的服务地址信息。
                        _dispatcher->registerHandler<BaseMessage>(MType::RSP_SERVICE, rsp_cb);

                        auto req_cb = std::bind(&client::Discovery::onServiceReuqest, _discovery.get(), 
                            std::placeholders::_1, std::placeholders::_2);
                        
                        // 当客户端收到一个服务下线通知请求时，onServiceReuqest 会处理这个响应，例如获得下线通知
                        // registerHandler<ServiceReuqest> 要注意对象实例化方式
                        _dispatcher->registerHandler<ServiceRequest>(MType::REQ_SERVICE, req_cb);

                        auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                            std::placeholders::_1, std::placeholders::_2);
                        _client = ClientFactory::create(ip, port);
                        _client->setMessageCallback(message_cb);
                        _client->connect(); // 连接注册中心
                    }
                // 向外提供的服务发现接口
                bool serviceDiscovery(const std::string &method, Address &host) {
                    return _discovery->discoveryMethod(_client->connection(), method, host);
                }
            private:
                Requestor::ptr _requestor;
                client::Discovery::ptr _discovery;
                Dispatcher::ptr _dispatcher;
                BaseClient::ptr _client; 
        };
        // 对外提供的rpc客户端
        // 客户端的功能：
        // 1. 连接服务注册中心
        // 2. 服务发现
        // 3. 调用服务
        // 4. 处理服务响应
        // 5. 处理服务下线通知
  
        class RpcClient {
            public:
                using ptr = std::shared_ptr<RpcClient>;
                // enableDiscovery是否启动服务发现功能，传入的地址和端口是服务注册中心的地址，还是服务提供者
                // enableDiscovery由用户来决定
                RpcClient(bool enableDiscovery,const std::string& ip, int port) :
                    _enableDiscovery(enableDiscovery),
                    _requestor(std::make_shared<Requestor>()),
                    _dispatcher(std::make_shared<Dispatcher>()),
                    _caller(std::make_shared<RpcCaller>(_requestor)) {
                    
                    // 针对 rpc 请求后的响应进行的回调处理
                    auto rsp_cb = std::bind(&Requestor::onResponse, _requestor.get(),
                        std::placeholders::_1, std::placeholders::_2);
                    _dispatcher->registerHandler<BaseMessage>(MType::RSP_RPC, rsp_cb);

                    // 如果启用了服务发现， 地址信息是注册中心的地址，是服务发现客户端需要连接的地址，
                    // 则通过地址信息实例化discovery_client
                    // 如果没有启用服务发现，地址信息是服务提供者的地址，则直接实例化好rpc_client
                    if (_enableDiscovery) {
                        auto offline_cb = std::bind(&RpcClient::delClient, this, std::placeholders::_1);
                        _discovery_client = std::make_shared<DiscoveryClient>(ip, port, offline_cb);
                    } else {
                        auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                            std::placeholders::_1, std::placeholders::_2);
                        _rpc_client = ClientFactory::create(ip, port);
                        _rpc_client->setMessageCallback(message_cb);
                        _rpc_client->connect();
                    }
                }
                bool call(const std::string &method,const Json::Value &params, Json::Value &result) {
                    //获取服务提供者：1. 服务发现；  2. 固定服务提供者
                    BaseClient::ptr client = getClient(method);
                    if (client.get() == nullptr) {
                        return false;
                    }
                    //3. 通过客户端连接， 发送rpc请求
                    return _caller->call(client->connection(), method, params, result);
                }
                bool call(const std::string &method,const Json::Value &params, RpcCaller::JsonAsyncResponse &result) {
                    BaseClient::ptr client = getClient(method);
                    if (client.get() == nullptr) {
                        return false;
                    }
                    //3. 通过客户端连接， 发送rpc请求
                    return _caller->call(client->connection(), method, params, result);
                }
                bool call(const std::string &method,const Json::Value &params, const RpcCaller::JsonResponseCallback &cb) {
                    BaseClient::ptr client = getClient(method);
                    if (client.get() == nullptr) {
                        return false;
                    }
                    //3. 通过客户端连接， 发送rpc请求
                    return _caller->call(client->connection(), method, params, cb);
                }

            private:
                BaseClient::ptr newClient(const Address& host) {
                    auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                            std::placeholders::_1, std::placeholders::_2);
                    auto client = ClientFactory::create(host.first, host.second);
                    client->setMessageCallback(message_cb);
                    client->connect();
                    putClient(host, client);
                    return client;
                }
                BaseClient::ptr getClient(const Address& host) {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    auto it = _rpc_clients.find(host);
                    if (it == _rpc_clients.end()) {
                        return BaseClient::ptr();
                    }
                    return it->second;
                }
                BaseClient::ptr getClient(const std::string& method) {
                    // enableDiscovery是否启动服务发现功能，传入的地址和端口是服务注册中心的地址，还是服务提供者
                    BaseClient::ptr client;
                    if (_enableDiscovery) {
                        // 1.通过服务发现，提供服务提供者地址信息
                        Address host;
                        bool ret = _discovery_client->serviceDiscovery(method,host);
                        if (ret == false) {
                            ELOG("当前 %s 服务， 没有找到服务提供者！", method.c_str());
                            return BaseClient::ptr();
                        }
                        client = getClient(host);
                        if (client.get() == nullptr) { //没有到自已实例化的客户端，则创建
                            client = newClient(host);
                        }
                    } else {
                        // 2. 查看服务发现是否已经实例化客户端，有则直接使用，没有则创建
                        client = _rpc_client;
                    }
                    return client;
                 } 
                void putClient(const Address& host, const BaseClient::ptr& client) {
                    std::unique_lock<std::mutex> _lock(_mutex);
                    _rpc_clients.insert(std::make_pair(host, client));
                }
                void delClient(const Address &host) {
                    std::unique_lock<std::mutex> lock(_mutex);
                    _rpc_clients.erase(host);
                }
            private:
                struct AddressHash {
                    std::size_t operator()(const Address &host) const {
                        std::string addr = host.first + std::to_string(host.second);
                        return std::hash<std::string>{}(addr);
                    }
                };
                bool _enableDiscovery;
                // 提供发现者客户端来管理方法和主机的映射关系
                DiscoveryClient::ptr _discovery_client;
                Requestor::ptr _requestor;
                RpcCaller::ptr _caller;
                Dispatcher::ptr _dispatcher;
                BaseClient::ptr _rpc_client; // 用于启用服务提供者的客户端
                std::mutex _mutex;
                // <"125.0.0.1:8080", client1>
                std::unordered_map<Address, BaseClient::ptr, AddressHash> _rpc_clients;
        };

        class TopicClient {
            public:
                using ptr = std::shared_ptr<TopicClient>;
                // 构造函数传入注册中心的地址信息，用于连接注册中心
                TopicClient(const std::string& ip, int port) :
                    _requestor(std::make_shared<Requestor>()),
                    _topic_manager(std::make_shared<TopicManager>(_requestor)),
                    _dispatcher(std::make_shared<Dispatcher>())
                    {
                        auto rsp_cb = std::bind(&client::Requestor::onResponse, _requestor.get(), 
                            std::placeholders::_1, std::placeholders::_2);
                        
                        // 当客户端收到一个服务响应时，  Requestor   会处理这个响应，例如解析返回的服务地址信息。
                        _dispatcher->registerHandler<BaseMessage>(MType::RSP_TOPIC, rsp_cb);

                        auto req_cb = std::bind(&client::TopicManager::onPublish, _topic_manager.get(), 
                            std::placeholders::_1, std::placeholders::_2);
                        
                        // 当客户端收到一个服务请求时，  Requestor   会处理这个响应，
                        _dispatcher->registerHandler<TopicRequest>(MType::REQ_TOPIC, req_cb);

                        auto message_cb = std::bind(&Dispatcher::onMessage, _dispatcher.get(),
                            std::placeholders::_1, std::placeholders::_2);
                        _client = ClientFactory::create(ip, port);
                        _client->setMessageCallback(message_cb);
                        _client->connect(); // 连接注册中心
                    }
                // 向外提供的主题操作接口
                bool create(const std::string &key) {
                    return _topic_manager->create(_client->connection(), key);
                }
                bool remove(const std::string &key) {
                    return _topic_manager->remove(_client->connection(), key);
                }
                bool subscribe(const std::string &key, const TopicManager::SubCallback& cb) {
                    return _topic_manager->subscribe(_client->connection(), key,cb);
                }
                bool cancel(const std::string &key) {
                   return _topic_manager->cancel(_client->connection(), key);
                }
                bool publish(const std::string &key, const std::string &msg) {
                    return _topic_manager->publish(_client->connection(),key, msg);
                }
                void shutdown() {
                    _client->shutdown();
                }
            private:
                Requestor::ptr _requestor;
                client::TopicManager::ptr _topic_manager;
                Dispatcher::ptr _dispatcher;
                BaseClient::ptr _client;                   
        };
    }
}
